package com.camel.chart.func;

import cn.hutool.core.util.ObjectUtil;
import com.camel.chart.MainApplication;
import com.camel.chart.call.DialogOkAfterCall;
import com.camel.chart.enums.MsgTypeEnum;
import com.camel.chart.utils.DialogUtil;
import com.camel.chart.utils.FileUtil;
import com.camel.chart.utils.MsgUtil;
import com.camel.chart.utils.SerializeUtil;
import com.camel.chart.utils.entity.*;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;

public class UdpServer implements Runnable {
    private DatagramSocket datagramSocket = null;

    private MainApplication application;

    private static final String filePath = "D:/IamFxFiles";


    // 定义构造方法
    public UdpServer(DatagramSocket datagramSocket, MainApplication application) {
        this.datagramSocket = datagramSocket;
        this.application = application;
    }

    public MainApplication getApplication() {
        return application;
    }

    public void setApplication(MainApplication application) {
        this.application = application;
    }

    public static int size = 0;

    // 线程
    @Override
    public void run() {
        try {
            while (true) {
                // 定义一个1024到字节数组，用于存放发送过来到数据
                byte[] data = new byte[41960];
                // new 一个数据报
                DatagramPacket datagramPacket = new DatagramPacket(data, data.length);
                // 阻塞等待数据发送过来
                datagramSocket.receive(datagramPacket);
                // 获取发送过来的数据的IP地址 和 端口号
                String ip = datagramPacket.getAddress().getHostAddress();
                int port = datagramPacket.getPort();
                // 获取传送过来的数据
                byte[] packetData = datagramPacket.getData();


                // 如果传送过来的数据是 bye 那么跳出循环 不在接收消息 结束
                // 用来显示发送过来的 IP地址 端口号 内容
                try {
                    Msg msg = Msg.deserialize(Arrays.copyOfRange(packetData, 0, 1000));
                    if (msg.getType().equals(MsgTypeEnum.TYPE_FILE_MSG)) {
                        handleFile(msg, Arrays.copyOfRange(packetData, 1000, 41960));
                    } else {
                        msg.setDataTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                        msg.setOriginAddr(ip);
                        msg.setTargetAddr(ApplicationConfig.getInstance().getChartNetWorkInterface().getInetAddress());
                        msgHandler(ip, port, msg);
                    }


                } catch (ClassNotFoundException e) {
                    datagramSocket.close();
                    throw new RuntimeException(e);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            datagramSocket.close(); // 关闭资源
        }
    }


    /**
     * 消息回调
     */
    private void msgHandler(String ip, int port, Msg msg) throws IOException {
        MsgTypeEnum type = msg.getType();
        if (MsgTypeEnum.TYPE_LOGIN.equals(type)) {
            // 需要回应
            Msg params = new Msg();
            params.setType(MsgTypeEnum.TYPE_LOGIN_CALLBACK);
            MsgUtil.getInstance().send(params, this.getApplication().chartContent);
        }
        if (MsgTypeEnum.TYPE_LOGOUT.equals(type)) {
            Vector<NetDevice> devices = ApplicationConfig.getInstance().getDevices();
            NetDevice netDevice = new NetDevice(ip, false);
            for (NetDevice device : devices) {
                if (device.equals(netDevice)) {
                    device.setOnline(false);
                }
            }
        }
        if (MsgTypeEnum.TYPE_LOGIN_CALLBACK.equals(type)) {
            Vector<NetDevice> devices = ApplicationConfig.getInstance().getDevices();
            if (!devices.contains(new NetDevice(ip))) {
                devices.add(new NetDevice(ip, true));
            } else {
                NetDevice netDevice = devices.get(devices.indexOf(new NetDevice(ip)));
                netDevice.setOnline(true);
            }
            SerializeUtil.serializeObject(devices, CommonConstants.DEFAULT_CONFIG_DEVICE_SERIALIZABLE_FILE);
        }
        if (MsgTypeEnum.TYPE_FILE_REQ.equals(type)) {
            //确认
            msg.setLeft(true);
            MsgUtil.getInstance().addMsgUI(getApplication().chartContent, msg);
            ChartHistory.getInstance().getMsges().get(ip).add(msg);
            SerializeUtil.serializeObjAppendToFile(ip + ".txt", "history", msg);
        }
        if (MsgTypeEnum.TYPE_FILE_ACK.equals(type)) {
            FileUtil.FileEntity fileEntity = (FileUtil.FileEntity) msg.getMsg();
            //发送文件
            Msg fileMsg = new Msg();
            fileMsg.setType(MsgTypeEnum.TYPE_FILE_MSG);
            fileMsg.setMsg(fileEntity);
            MsgUtil.getInstance().sendTcpMsg(fileMsg, this.getApplication().chartContent);
            // 更新UI
            MsgUtil.getInstance().updateMsgUI(getApplication().chartContent, msg, false);
        }
        if (MsgTypeEnum.TYPE_FILE_MSG.equals(type)) {
            //准备工作
            File dir = new File(filePath);
            FileUtil.FileEntity fileEntity = (FileUtil.FileEntity) msg.getMsg();
            if (!dir.exists()) {
                dir.mkdir();
            }
            File file = new File(dir, fileEntity.getFileName());
            file.createNewFile();


        }
        if (MsgTypeEnum.TYPE_FILE_EOF.equals(type)) {

        }

        if (MsgTypeEnum.TYPE_TEXT_MSG.equals(type)) {
            if (!ChartHistory.getInstance().getMsges().containsKey(ip)) {
                ChartHistory.getInstance().getMsges().put(ip, new ArrayList<>());
            }
            msg.setLeft(true);
            Platform.runLater(() -> {
                ScrollPane scrollPane = this.getApplication().chartContent;
                VBox v = new VBox();
                HBox msgDateUI = MsgUtil.getInstance().createMsgDateUI(msg);
                if (ObjectUtil.isNotNull(msgDateUI)) {
                    msgDateUI.setPadding(new Insets(15, 15, 0, 15));
                    if (ObjectUtil.isEmpty(scrollPane.getContent())) {
                        v.getChildren().add(msgDateUI);
                        scrollPane.setContent(v);
                    } else {
                        VBox box = (VBox) scrollPane.getContent();
                        box.getChildren().add(msgDateUI);
                    }
                }

                HBox detailUI = MsgUtil.getInstance().createMsgDetailUI(msg);
                if (ObjectUtil.isNotNull(detailUI)) {
                    detailUI.setPadding(new Insets(3, 15, 0, 15));
                    if (ObjectUtil.isEmpty(scrollPane.getContent())) {
                        v.getChildren().add(detailUI);
                        scrollPane.setContent(v);
                    } else {
                        VBox box = (VBox) scrollPane.getContent();
                        box.getChildren().add(detailUI);
                    }
                }
            });

            ChartHistory.getInstance().getMsges().get(ip).add(msg);
            SerializeUtil.serializeObjAppendToFile(ip + ".txt", "history", msg);
        }

    }

    private void handleFile(Msg msg, byte[] bytes) throws IOException {
        //接收文件
        FileUtil.FileEntity fileEntity = (FileUtil.FileEntity) msg.getMsg();
        File file = new File(filePath, fileEntity.getFileName());
        if (!file.exists()) {
            // TODO 文件已存在
            file.createNewFile();
        }
        RandomAccessFile randomAccessFile = new RandomAccessFile(file.getPath(), "rw");
        FileChannel channel = null;
        try {
            channel = randomAccessFile.getChannel();
            channel.position(channel.size());
            channel.write(ByteBuffer.wrap(bytes));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            channel.close();

        }
    }
}
